﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using MapInfo.Wrapper.MapOperations;

namespace NathansMapInfoProjects.WindowManager.ViewModels
{
    /// <summary>
    /// View model for a group in the tree view.
    /// </summary>
    public class GroupViewModel : TreeViewItemViewModel
    {
        private bool _isEditingName;
        private bool NamesNeedUpdate;

        public GroupViewModel(string groupName, IEnumerable<WindowTypes> types)
            :base(null, false)
        {
            this.Name = groupName;

            this.SortAcc = new RelayCommand(obj => SortItems(SortOrder.Ascending));
            this.SortDes = new RelayCommand(obj => SortItems(SortOrder.Descending));

            // Command that switches us into editing mode.
            // this.SwitchToEditingMode = new DelegatingCommand(() => IsEditingName = !IsEditingName);

            this.Types = new List<WindowTypes>();
            this.Types.AddRange(types);

            // Wire up the events to handle open and closed from MapInfo.
            Entry.WindowFocusChanged += new Action<Window>(Entry_WindowFocusChanged);
            Entry.WindowClosed += new Action<Window>(Entry_WindowClosed);


            this.Children.CollectionChanged += Children_CollectionChanged;
        }

        /// <summary>
        /// If the collection has been updated we need to update the name also showing the count.
        /// We can do this by calling the NameWithCount property changed event to make the view rebind the
        /// value.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.OnPropertyChanged("NameWithCount");
        }

        /// <summary>
        /// Handles the window closed event in MapInfo.
        /// </summary>
        /// <param name="window">The window in MapInfo that was closed.</param>
        void Entry_WindowClosed(Window window)
        {
            // Check to see if this group handles this type of window, if it doesn't
            // we can just exit.
            if (this.Types.Contains(window.Type))
            {
                WindowViewModel item = this.Children.FirstOrDefault(win => win.ID == window.ID) as WindowViewModel;
                
                if (item != null)
                {
                    this.RemoveWindowFromGroup(item);
                    this.NamesNeedUpdate = true;
                }
            }
        }

        void Entry_WindowFocusChanged(Window window)
        {
            // To handle the issue of not being able to get the updated names
            // of the windows in the closed window event, we have to use a flag to make sure that
            // we update the name when the focus changes.
            if (this.NamesNeedUpdate)
            {
                this.UpdateWindowNames();
                return;
            }

            // Check to see if we handle the window type in this group.
            // We use the Types enum flag to check.
            if (this.Types.Contains(window.Type))
            {
                WindowViewModel item = this.Children.FirstOrDefault(win => win.ID == window.ID) as WindowViewModel;

                // If the item is null it means that the window doens't exsits in this group yet.
                if (item == null)
                {
                    item = new WindowViewModel(this, window);
                    this.AddWindowToGroup(item);
                }

                item.IsSelected = true;
            }
        }

        /// <summary>
        /// The collection of <see cref="WindowTypes"/> that this group
        /// handles.
        /// </summary>
        public List<WindowTypes> Types { get; private set; }

        private string name;

        /// <summary>
        /// Gets or sets the name of the group.
        /// </summary>
        public override string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
                base.OnPropertyChanged("Name");
                base.OnPropertyChanged("NameWithCount");
                this._isEditingName = false;
            }
        }

        /// <summary>
        /// Gets the name of the group with the count of items appened.
        /// </summary>
        public string NameWithCount
        {
            get
            {
                return this.Name + " (" + this.Children.Count + ")";
            }
        }

        /// <summary>
        /// Gets or sets if the view model is in edit mode.
        /// </summary>
        public bool IsEditingName
        {
            get { return _isEditingName; }
            set { _isEditingName = value; OnPropertyChanged("IsEditingName"); }
        }

        
        public void AddWindowToGroup(WindowViewModel window)
        {
            this.Children.Add(window);
            this.UpdateWindowNames();
            window.WindowClosed += new Action<WindowViewModel>(WindowClosed);
        }

        /// <summary>
        /// Removes the window from collection and flags the names to be updated.
        /// </summary>
        /// <param name="window">The windows view model to be removed from the
        /// collection.</param>
        public void RemoveWindowFromGroup(WindowViewModel window)
        {
            this.Children.Remove(window);
        }

        /// <summary>
        /// Event to handle when a window is closed using the close command on the
        /// view model.
        /// </summary>
        /// <param name="obj"></param>
        void WindowClosed(WindowViewModel obj)
        {
            this.RemoveWindowFromGroup(obj);
            this.UpdateWindowNames();
        }

        /// <summary>
        /// Sorts the items in the current collection.
        /// </summary>
        /// <param name="order">The order to sort the windows.</param>
        public void SortItems(SortOrder order)
        {
            this.Children.Sort(win => win.Name, order);
        }

        /// <summary>
        /// Command used to switch to editing mode
        /// </summary>
        public ICommand SwitchToEditingMode { get; private set; }

        /// <summary>
        /// Updates all the names of the windows in the group.  Used when new windows are opened or closed.
        /// </summary>
        public void UpdateWindowNames()
        {
            // Raise the property changed event for all the properties on the children to update
            // the tree view.
            // We do this here because when a new map opens any maps with the same name now have :1 , :2 
            // at the end and we need to see this.
            // Eg MyMap:1, MyMap:2
            foreach (var window in this.Children)
            {
                window.OnPropertyChanged("");
            }

            this.NamesNeedUpdate = false;
        }

        public override void LoadChildren()
        {
            this.Children.Clear();

            foreach (WindowTypes type in this.Types)
            {
                var windows = Entry.Session.Windows.Where(win => win.Type == type);

                if (windows != null)
                {
                    foreach (Window window in windows)
                    {
                        WindowViewModel windowvm = new WindowViewModel(this, window);
                        this.AddWindowToGroup(windowvm);
                    }
                }
            }
        }

        /// <summary>
        /// Command bound to by the view to sort the items in ascending order.
        /// </summary>       
        public ICommand SortAcc { get; private set; }

        /// <summary>
        /// Command bound to by the view to sort the items in descending order.
        /// </summary>
        public ICommand SortDes { get; private set; }
    }
}
